import { build as buildQueryString } from '@m7/qs';

function noop () {}

export function ajax (settings = {}) {
  const {
    method = 'GET',
    async = true,
    context,
    headers = {},
    dataType = 'application/json',
    contentType,
    withCredentials = false,
    onComplete = noop,
    onSuccess = noop,
    onError = noop,
    onTimeout = noop,
    data,
    timeout
  } = settings;

  const startTime = +new Date();
  const isGet = method === 'GET';
  const xhr = new XMLHttpRequest();
  let response;
  let url = settings.url || location.toString();
  let isComplete = false;
  let timer;
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
      if (
        (xhr.status >= 200 && xhr.status < 300) ||
        xhr.status === 304 ||
        xhr.status === 0
        ) {
        response = xhr.responseText.replace(/(\r|\n|\t)/gi, '');
        if (dataType === 'application/json') {
          try {
            response = JSON.parse(response);
          } catch (e) {
            throw Error(e);
          }
        }
        onSuccess.call(context || xhr, response, xhr);
      } else {
        onError.call(context || xhr, xhr, +new Date() - startTime);
      }
      isComplete = true;
      onComplete.call(context || xhr, +new Date() - startTime);
      clearTimeout(timer);
    }
  };
  if (isGet && data) {
    url = `${url}${url.indexOf('?') > -1 ? '&' : '?'}${buildQueryString(data)}`;
  }
  if (withCredentials) {
    xhr.withCredentials = true;
  }
  xhr.open(method, url, async);
  if (contentType) {
    xhr.setRequestHeader('Content-Type', contentType);
  }
  Object.keys(headers).forEach((header) => {
    xhr.setRequestHeader(header, `${headers[header]}`);
  });
  if (isGet || !data) {
    xhr.send();
  } else if (/application\/json/i.test(contentType)) {
    xhr.send(JSON.stringify(data));
  } else if (!isGet && !contentType) {
    xhr.send(data);
  } else {
    xhr.send(buildQueryString(data));
  }
  if (timeout) {
    timer = setTimeout(() => {
      if (!isComplete) {
        xhr.abort();
        onTimeout.call(context || xhr, xhr);
      }
    }, timeout);
  }
  return xhr;
}

export function request (settings = {}) {
  return new Promise((resolve, reject) => {
    ajax(Object.assign(settings, {
      onSuccess (resp) {
        resolve(resp);
      },
      onError (xhr) {
        reject(xhr);
      },
      onTimeout (xhr) {
        reject(xhr);
      }
    }));
  });
}

export function get (url, data, settings = {}) {
  return request(Object.assign(settings, {
    url,
    data,
    method: 'GET'
  }));
}

export function post (url, data, settings = {}) {
  return request(Object.assign(settings, {
    url,
    data,
    method: 'POST'
  }));
}
